Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(angular.copy) support for Map and Set #15697

Closed
wants to merge 3 commits into from
Closed

fix(angular.copy) support for Map and Set #15697

wants to merge 3 commits into from

Conversation

mrohr
Copy link

@mrohr mrohr commented Feb 9, 2017

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
bug fix

What is the current behavior? (You can also link to an open issue here)
the copied Map or Set will fail to call functions with an error such as:
TypeError: Method Map.prototype.size called on incompatible receiver #

What is the new behavior (if this is a feature change)?
angular.copy will now property create new Map or Set objects with the same values as the source.

Does this PR introduce a breaking change?
No

Please check if the PR fulfills these requirements

Other information:

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

1 similar comment
@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@mrohr
Copy link
Author

mrohr commented Feb 9, 2017

signed!

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.

1 similar comment
@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.

@mrohr
Copy link
Author

mrohr commented Feb 9, 2017

updated email for CLA

@googlebot
Copy link

CLAs look good, thanks!

1 similar comment
@googlebot
Copy link

CLAs look good, thanks!

@googlebot googlebot added cla: yes and removed cla: no labels Feb 9, 2017
@frederikprijck
Copy link
Contributor

frederikprijck commented Feb 10, 2017

@mrohr After you asked on gitter, I did some digging in the reason why the tests was failing in IE11, it looks like Object.prototype.toString.call(source) (https://github.com/mrohr/angular.js/blob/d5dbb5973b4588e75afee9d81a0f008f69dc033d/src/Angular.js#L997) evaluates to [object Object] for both Map and Set in IE11, resulting in the usage of Object.create(Object.getPrototypeOf(source) (https://github.com/mrohr/angular.js/blob/d5dbb5973b4588e75afee9d81a0f008f69dc033d/src/Angular.js#L984) to copy instead of the map/set code.
This throws the exception you're getting when accessing size on the destination object:

Map.prototype.size: 'this' is not a Map object.

I created a fiddle to demonstrate the issue outside angularjs: https://jsfiddle.net/0mstLj6q/7/

Running this plunkr in IE11 (Windows 10) shows:

image

Running this in Chrome shows:

image

case '[object Map]':
// If we're in this case we know the environment supports Map
/* eslint-disable no-undef */
var copiedMap = new Map();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We access globals through the window in order to support non-browser environments (such as jsdom).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had originally used new source.constructor() here, tried changing it to Map/Set to deal /w the IE11 issue that is failing the build. My next update I will change it back

/* eslint-disable no-undef */
var copiedMap = new Map();
/* eslint-enable */
source.forEach(function(value, key) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't return new Map(source) work as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand this correctly, IE11 does not support Map and Set constructor with arguments ? https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Map

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to copy the keys/values, not just assign them, probably by invoking copyRecurse so we also get the maxDepth behavior.

@mrohr
Copy link
Author

mrohr commented Feb 21, 2017

@frederikprijck Thanks for your help, I'm guessing this feature is pretty much stuck, unless we set up the tests to not run in browsers that don't print map/set objects .toString() as '[object Map]'/'[object Set]'?

@frederikprijck
Copy link
Contributor

frederikprijck commented Feb 21, 2017

Or are there alternative approaches available to detect Map and Set in IE11 (or all browsers)?

I just did some debugging and this validates to true in Chrome and IE11:

source.constructor === Map;
Object.getPrototypeOf(source) === Map.prototype;

aswell as

source.constructor === Set;
Object.getPrototypeOf(source) === Set.prototype;

@gkalpak gkalpak added this to the Ice Box milestone Feb 28, 2017
@gkalpak
Copy link
Member

gkalpak commented Feb 28, 2017

There are a couple of problems with this feature (which are not specific to the feature itself, rather general problems with similar features):

  1. In order to be able to reliably use the feature throughout AngularJS, other internal helpers need to support it too (e.g. equals()). Supporting a feature in copy() only, is useful when using copy() as a general helper utility, but this is not its purpose and adds extra size and maintenance overhead.

  2. It is really difficult to ensure that the feature works reliably and consistently across browsers and browser versions, because it relies on JS features that are not supported by all browsers, are fairly new additions (which means browser implementations are buggy and may change frequently) and are only partially supported by some browsers (which makes it even more difficult to rely upon or polyfill).

For these reasons, I am going to put it in the Ice Box for now (but I am open to being convinced otherwise - now or in the future 😃).
In any case, thx for working on this @mrohr 👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants